package iit.projects.etaxi.model.dao;

import iit.projects.etaxi.model.dto.ReservationSCDO;
import iit.projects.etaxi.model.entity.Customer;
import iit.projects.etaxi.model.entity.Reservation;
import iit.projects.etaxi.model.exception.DuplicateReservationException;
import iit.projects.etaxi.model.exception.ReservationNotFoundException;

import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class HibernateReservationDao extends HibernateDaoSupport implements
		ReservationDao {
	
	final Logger log = LoggerFactory.getLogger(HibernateReservationDao.class);

	@SuppressWarnings("unchecked")
	@Override
	public Reservation createReservation(Reservation reservation) throws DuplicateReservationException {
		if(log.isDebugEnabled()){
			log.debug("Going to create Reservation " + reservation.getCode());
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(Reservation.class);
			criteria.add(Restrictions.eq("code", reservation.getCode()));
			List<Reservation> list = criteria.list();
			if(list != null && !list.isEmpty()){
				throw new DuplicateReservationException(reservation.getCode());
			}
			return (Reservation)session.merge(reservation);
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Reservation findByCode(String code) throws ReservationNotFoundException {
		if(log.isDebugEnabled()){
			log.debug("Going to find Reservation by code" + code);
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(Reservation.class);
			criteria.add(Restrictions.eq("code", code));
			List<Reservation> list = criteria.list();
			if(list != null && !list.isEmpty()){
				return list.iterator().next();
			}
			else{
				throw new ReservationNotFoundException(new String[]{code});
			}
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	
	@Override
	@SuppressWarnings("unchecked")
	public List<Reservation> findReservationsByStatusAndTime(String status, Date startTime, Date endTime){
		
		Session session = getSession(false);
		
		if(log.isDebugEnabled()){
			log.debug("Going to find " + status + " Reservations between " + startTime + " and " + endTime);
		}
		try{
			Criteria criteria = session.createCriteria(Reservation.class);
			criteria.add(Restrictions.eq("status", status));
			criteria.add(Restrictions.between("pickUpTime", startTime, endTime));
			List<Reservation> reservations = criteria.list();
			
			if(log.isDebugEnabled()){
				log.debug("Returning " + reservations.size() + " Reservation(s)");
			}
			
			return reservations;
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	@Override
	public Reservation updateReservation(Reservation reservation){
		if(log.isDebugEnabled()){
			log.debug("Going to update Reservation " + reservation.getCode());
		}
		
		Session session = getSession(false);
		try{
			return (Reservation)session.merge(reservation);
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Reservation> searchReservations(ReservationSCDO reservationSCDO){
		if(log.isDebugEnabled()){
			log.debug("Going to find Reservations");
		}
		
		Session session = getSession(false);
		
		try{
			String code = reservationSCDO.getCode();
			Date startDate = reservationSCDO.getStartingDate();
			Date endDate = reservationSCDO.getEndingDate();
			Customer customer = reservationSCDO.getCustomer();
			
			Criteria criteria = session.createCriteria(Reservation.class);
			if (code != null && !code.isEmpty()) {
				if (!code.contains("%")) {
					code = "%" + code + "%";
				}
				criteria.add(Restrictions.like("code", code));
			}
			if(customer != null){
				criteria.add(Restrictions.eq("customer", customer));
			}
			if(startDate != null && endDate != null){
				criteria.add(Restrictions.between("pickUpTime", startDate, endDate));
			}
			else if(startDate != null){
				criteria.add(Restrictions.gt("pickUpTime", startDate));
			}
			else if(endDate != null){
				criteria.add(Restrictions.lt("pickUpTime", endDate));
			}
			
			List<Reservation> list = criteria.list();
			return list;
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<Reservation> findReservationsBefore(String status, Date time){
		if(log.isDebugEnabled()){
			log.debug("Going to find " + status + " Reservations before " + time);
		}
		
		Session session = getSession(false);
		
		try{
			Criteria criteria = session.createCriteria(Reservation.class);
			criteria.add(Restrictions.eq("status", status));
			criteria.add(Restrictions.lt("lastProcessedTime", time));
			
			List<Reservation> reservations = criteria.list();
			return reservations;
		}catch(HibernateException ex){
			throw convertHibernateAccessException(ex);
		}
		
	}

}
